package com.beidu.lottery.call.service.portal.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.beidu.lottery.call.common.BaseContextHandler;
import com.beidu.lottery.call.constant.ActivityTypeEnum;
import com.beidu.lottery.call.constant.CacheKey;
import com.beidu.lottery.call.errorcode.CommonCode;
import com.beidu.lottery.call.constant.MQTopicsConst;
import com.beidu.lottery.call.dao.ParticipationRegDetailMapper;
import com.beidu.lottery.call.dao.ParticipationRegMapper;
import com.beidu.lottery.call.exception.ServiceException;
import com.beidu.lottery.call.service.portal.ActivityRegService;
import com.beidu.lottery.call.service.portal.ActivityRuleService;
import com.beidu.lottery.call.service.portal.UserDataService;
import com.beidu.lottery.call.utils.ActSequenceGen;
import com.beidu.lottery.call.entity.ParticipationReg;
import com.beidu.lottery.call.entity.ParticipationRegDetail;
import com.beidu.lottery.call.entity.PortalActivityRule;
import com.beidu.lottery.call.entity.dto.ActivityRegDTO;
import com.beidu.lottery.call.entity.vo.ActivityRegResultVO;
import com.beidu.lottery.call.utils.StockRedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * <p>
 * 报名活动 服务实现类
 * </p>
 *
 * @author 北渡
 * @since 2023-08-23
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ActivityRegServiceImpl implements ActivityRegService {
    private final StockRedisUtil stockRedisUtil;
    private final KafkaTemplate<String, String> kafkaTemplate;
    private final ParticipationRegMapper participationRegMapper;
    private final ParticipationRegDetailMapper participationRegDetailMapper;
    private final UserDataService userDataService;
    private final ActivityRuleService activityRuleService;

    @Override
    public ActivityRegResultVO regPreSubmit(ActivityRegDTO regDTO, PortalActivityRule rule) {
        // 减扣名额
        if (!stockRedisUtil.decrLimit(CacheKey.genRegCounter(regDTO.getActivityId()), 1)) {
            throw new ServiceException(CommonCode.REG_QUOTA_FULL.getMsg());
        }
        log.info("Redis中报名名额-1");

        // 生成报名编号（MQ消息ID）
        String regNumber = ActSequenceGen.genRegNumber(regDTO.getActivityId());
        log.info("生成报名编号, regNumber => {}", regNumber);
        regDTO.setBaseDTO(regNumber);// 设置用户信息上下文+MQ消息ID

        // 组装MQ消息
        JSONObject data = new JSONObject();
        data.put("dto", regDTO);
        data.put("rule", rule);
        log.info("组装预报名MQ消息完成, data => {}", data);

        // 发送Kafka消息
        try {
            kafkaTemplate.send(MQTopicsConst.ACTIVITY_REG_SUBMIT, data.toJSONString());
        } catch (Exception e) {
            log.error("消息发送异常, 回滚名额, activityID => {}", regDTO.getActivityId(), e);
            // 回滚名额
            stockRedisUtil.incr(CacheKey.genRegCounter(regDTO.getActivityId()), 1);
            // 返回错误信息
            throw new ServiceException(CommonCode.RATE_LIMITER.getMsg());
        }

        // 组装VO
        ActivityRegResultVO result = ActivityRegResultVO.builder()
                .regNumber(regNumber)
                .operationTips(rule.getOperationTips())
                .postActivityId(rule.getPostActivityId()).build();
        log.info("预报名完成, result => {}", result);
        return result;
    }

    // seta分布式事务注解：@GlobalTransactional(name = "activity_lottery_submit_logging", rollbackFor = Exception.class)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void regSubmit(ActivityRegDTO regDTO, PortalActivityRule rule) {
        log.info("regSubmit, regDTO => {}", regDTO);

        // 保存报名基础信息
        ParticipationReg reg = ParticipationReg.builder()
                .tenantCode(regDTO.getTenantCode())
                .userId(regDTO.getUserId())
                .activityId(regDTO.getActivityId())
                .regNumber(regDTO.getMessageId())
                .points(rule.getConsumePoints()).build();
        participationRegMapper.insert(reg);
        log.info("保存报名基础信息完成, pId => {}", reg.getId());

        // 保存报名详情信息
        ParticipationRegDetail regDetail = ParticipationRegDetail.builder()
                .pId(reg.getId())
                .formData(regDTO.getFormData())
                .materialUrls(regDTO.getMaterialUrls())
                .coverUrl(regDTO.getCoverUrl()).build();
        participationRegDetailMapper.insert(regDetail);
        log.info("保存报名详情信息完成, detailId => {}", regDetail.getId());

        // 调用API, 消费积分（负数）
        if (rule.getConsumePoints() != null && rule.getConsumePoints() != 0) {
            userDataService.pointsOpSubmit(-rule.getConsumePoints(), regDTO.getActivityId());
        }
        log.info("报名入库完成, regNumber => {}", reg.getRegNumber());
    }

    @Override
    public boolean regUpdate(ActivityRegDTO regDTO) {
        log.info("regUpdate, regDTO => {}", regDTO);

        // 修改报名信息详情信息
        ParticipationRegDetail regDetail = ParticipationRegDetail.builder()
                .formData(regDTO.getFormData())
                .materialUrls(regDTO.getMaterialUrls())
                .coverUrl(regDTO.getCoverUrl())
                .updatedBy(BaseContextHandler.getUserId())
                .updatedDate(LocalDateTime.now()).build();
        int row = participationRegDetailMapper.update(regDetail, Wrappers.lambdaQuery(ParticipationRegDetail.class)
                .eq(ParticipationRegDetail::getPId, regDTO.getId()));

        log.info("更新报名信息详情信息完成, row => {}", row);
        return row > 0;
    }

    @Override
    public ActivityRegResultVO queryRegResult(Integer id) {
        // 查询报名基础信息
        ParticipationReg reg = participationRegMapper.selectOne(Wrappers.lambdaQuery(ParticipationReg.class)
                .eq(ParticipationReg::getActivityId, id)
                .eq(ParticipationReg::getUserId, BaseContextHandler.getUserId()));
        if (reg == null) {
            return null;
        }
        // 查询活动规则
        PortalActivityRule rule = activityRuleService.getActivityRuleCache(id, ActivityTypeEnum.REG);
        if (rule == null) {
            return null;
        }
        // 组装VO
        ActivityRegResultVO result = ActivityRegResultVO.builder()
                .id(reg.getId())
                .regNumber(reg.getRegNumber())
                .operationTips(rule.getOperationTips())
                .postActivityId(rule.getPostActivityId()).build();
        log.info("报名结果查询完成, result => {}", result);
        return result;
    }

    @Override
    public boolean hasRegistered(Integer id) {
        // 短期报名标记(防止用户维度线程并发)
        if (stockRedisUtil.incr(CacheKey.REQUEST_REG + BaseContextHandler.getUserId(), 1, 2) > 1) {
            return true;
        }
        // 查询数据库报名信息
        return participationRegMapper.selectCount(Wrappers.lambdaQuery(ParticipationReg.class)
                .eq(ParticipationReg::getUserId, BaseContextHandler.getUserId())
                .eq(ParticipationReg::getActivityId, id)) > 0;
    }

}
